﻿using System;
using System.Collections.Generic;
using ExplorerService.Common.ServiceContracts;

namespace ExplorerService.Managers
{
    /// <summary>
    /// Singleton used to manage upload and download streams for clients and applications
    /// </summary>
    public sealed class StreamManager
    {
        /// <summary>
        /// Stores the current instance
        /// </summary>
        private static StreamManager _instance;

        /// <summary>
        /// Object used in locks for simple thread safety
        /// </summary>
        private static readonly object Padlock = new object();

        /// <summary>
        /// Stores dictionary of upload stream IDs and the client callbacks
        /// </summary>
        readonly Dictionary<Guid, IClientStreamingServiceCallback> _uploadStreams = new Dictionary<Guid, IClientStreamingServiceCallback>();

        /// <summary>
        /// Stores dictionary of download stream IDs and the client callbacks
        /// </summary>
        readonly Dictionary<Guid, IClientStreamingServiceCallback> _downloadStreams = new Dictionary<Guid, IClientStreamingServiceCallback>();

        /// <summary>
        /// Default constructor
        /// </summary>
        StreamManager() { }

        /// <summary>
        /// Gets the instance of the stream manager
        /// </summary>
        public static StreamManager Instance
        {
            get
            {
                lock (Padlock) return _instance ?? (_instance = new StreamManager());

            }
        }

        /// <summary>
        /// Registers a new upload stream and returns the stream ID
        /// </summary>
        public Guid BeginUploadStream(Guid clientSessionId, IClientStreamingServiceCallback callbackChannel)
        {
            var id = Guid.NewGuid();
            _uploadStreams.Add(id, callbackChannel);
            return id;
        }

        /// <summary>
        /// Requests the next chunk from the client
        /// </summary>
        public byte[] GetNextStreamChunk(Guid streamId)
        {
            if (!_uploadStreams.ContainsKey(streamId)) throw new Exception("Invalid stream ID");
            return _uploadStreams[streamId].GetNextChunk();
        }

        /// <summary>
        /// Publishes a chunk to the client
        /// </summary>
        public void PublishChunk(Guid streamId, byte[] chunk, long bytesLeft)
        {
            if (!_downloadStreams.ContainsKey(streamId)) throw new Exception("Invalid stream ID");
            _downloadStreams[streamId].DownloadStreamChunkAvailable(chunk, bytesLeft);
        }

        /// <summary>
        /// Registers a new download stream and returns the stream ID
        /// </summary>
        public Guid BeginDownloadStream(Guid clientSessionId, IClientStreamingServiceCallback callbackChannel)
        {
            var id = Guid.NewGuid();
            _downloadStreams.Add(id, callbackChannel);
            return id;
        }
    }
}
